Skip to content

fix: upload pasted images into private SSH directories - #16523

Merged
teamleaderleo merged 2 commits into
mainfrom
fix/ssh-image-paste
Oct 1, 2026
Merged

teamleaderleo merged 2 commits into
mainfrom
fix/ssh-image-paste

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Cmd+V with an image now uploads the materialized clipboard file through the existing cmux SSH/SCP transfer path before inserting the remote path. Local terminals still insert the existing local temporary path and keep the existing local cleanup.

Remote uploads use a random per-session directory under ~/.cache/cmux/paste, with a 0700 directory and 0600 files. Every upload removes files older than 24 hours and trims the oldest files when the session directory exceeds 200 MB. Clean relay teardown removes only that session's paste files. Upload or permission failures call the existing failure path and insert no local path.

Sundial g1 🛠️

Relay authorization analysis

No relay method or relay allowlist entry was added. The implementation reuses the existing app-side RemoteSessionCoordinator.uploadDroppedFiles and detected-SSH SCP path. There is no new relay payload, command-bearing parameter, remote object selector, or remote state read. The coordinator owns a random session policy and teardown cleanup is scoped to that policy's directory.

Testing

  • Added package unit tests for path choice, random naming and extension sanitization, age cleanup, oldest-first size cleanup, private directory mode, and teardown cleanup.
  • Added a regression test that a remote upload failure inserts no local clipboard path.
  • Preserved the existing local versus remote planner tests and updated path assertions for the private remote destination.
  • python3 scripts/verify-local.py --affected upstream/main passes syntax, test wiring, package groups, and feature-flag checks.
  • Native compilation and tests must run through the managed fleet per repository instructions.

The first commit is the red policy test proof; the second commit implements the policy and transfer changes.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Pasted images in remote terminals now upload through the existing SSH/SCP transfer path instead of inserting a local clipboard path, so Cmd+V on an SSH session inserts the remote file.

Uploads land in a private per-session directory under ~/.cache/cmux/paste, enforced to 0700 directories and 0600 files. Each upload deletes files older than 24 hours and trims the oldest files past a 200 MB cap. Relay teardown removes only that session's paste files.

  • No relay method or allowlist entry was added; the change reuses the existing RemoteSessionCoordinator.uploadDroppedFiles and detected-SSH SCP paths.
  • Upload or permission failures use the existing failure path and insert no local path.

Written for commit 6df2cdf. Summary will update on new commits.

Review in cubic

teamleaderleo and others added 2 commits October 1, 2026 15:13
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo enabled auto-merge (squash) October 1, 2026 22:26
@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 1689f574-1b47-4982-9975-54fe11150f3a

📥 Commits

Reviewing files that changed from the base of the PR and between 53c705c and 6df2cdf.

📒 Files selected for processing (10)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+Lifecycle.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+Upload.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemotePasteFileTransferPolicyTests.swift
  • Sources/TerminalCustomUploadRunner.swift
  • Sources/TerminalSSHSessionDetector.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
  • cmuxTests/TerminalUploadCommandTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@teamleaderleo
teamleaderleo merged commit 8473bdc into main Oct 1, 2026
50 of 53 checks passed
@teamleaderleo
teamleaderleo deleted the fix/ssh-image-paste branch October 1, 2026 22:27
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Merge receipt for 6df2cdfbd6, merged 2026-10-01 22:27:07 UTC

  • Not verified at merge: ci-status (not reported), CI fast guards (in progress), GhosttyKit release check (in progress), guards (15) (in progress), swift-package-tests (in progress), Web complexity (in progress)
  • Verified: detect-ios-changes, Fast static checks, runner, web-validation
  • Skipped by policy: admission-placement, browser, Claude wrapper regressions, Dogfood build #​${{ github.event.pull_request.number }}, full-suite-coverage, remote-daemon, suite-coverage, web, web-build, web-database-tests, web-tests
  • Full suite: runs on main after merge.

Labeled merged-unverified: if main breaks near this merge, look here first.

@github-actions github-actions Bot added the merged-unverified A judging check was not green at merge; see the merge receipt comment label Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

main no longer compiles after this merge

@teamleaderleo: after 8473bdce0c landed on main, the app-host test product (the app and cmuxTests, build-for-testing) stops compiling. These errors first show up in a range of 9 merges (65930fc144..6d865370cd), and this pull request's diff is the one that reaches them. The other merges in that range (005906635c, c266af9d84, 4a46320d2b, e447665c96, eba3c42943, 53c705cd8f, 256d96435d, 6d865370cd) are being compiled on their own to confirm.

Evidence: https://github.com/manaflow-ai/cmux/actions/runs/36935336275/job/110614442984

Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift:16: error: struct 'UUID' is internal and cannot be referenced from a default argument value
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift:16: error: initializer 'init()' is internal and cannot be referenced from a default argument value
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift:9: error: property cannot be declared public because its type uses an internal type
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift:15: error: initializer cannot be declared public because its parameter uses an internal type
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemotePasteFileTransferPolicy.swift:26: error: method cannot be declared public because its parameter uses an internal type

Nothing blocks merging meanwhile. A fix-forward (or, failing that, a revert) is attempted automatically unless an open pull request already fixes this.

main_compile_attribution.py: post-merge, nothing here gates a merge.

teamleaderleo added a commit that referenced this pull request Oct 1, 2026
#16523 changed TerminalCustomUploadRunner to read
session.remotePastePolicy, but the runner has no session, so the app
target stopped compiling. Restore RemoteSessionCoordinator.remoteDropPath,
the package's compatibility entry point, which now returns the same
private-directory path shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK
teamleaderleo added a commit that referenced this pull request Oct 1, 2026
#16523 called CmuxRemoteSession's internal String.shellSingleQuoted from
the app target, which breaks main's compile. DetectedSSHSession already
has an identical private static shellSingleQuoted(_:); use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK
teamleaderleo added a commit that referenced this pull request Oct 1, 2026
#16523 changed TerminalCustomUploadRunner to read
session.remotePastePolicy, but the runner has no session, so the app
target stopped compiling. Restore RemoteSessionCoordinator.remoteDropPath,
the package's compatibility entry point, which now returns the same
private-directory path shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK
rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 1, 2026
0906bcb fix: make main's full test suite pass again (manaflow-ai#16429)
11bfe00 Restore custom sidebar preview gallery (manaflow-ai#16535)
343dd1b web: sync all Hexclave webhooks into a validated, order-independent mirror (manaflow-ai#16339)
00547d5 ci: avoid blaming unrelated merges for compile failures (manaflow-ai#16533)
b782440 fix(ci): provision Go for every iOS Release archive (manaflow-ai#16534)
3555618 Add a Jump to Bottom button to terminal panes (manaflow-ai#15382)
79febcf fix: tolerate delayed App Store Connect processing (manaflow-ai#16527)
fcbf13c fix: export Foundation for remote paste policy (manaflow-ai#16525)
6d86537 Add What's New recap with an off / quiet / sheet setting (manaflow-ai#14876)
256d964 fix(xcstrings): keep conflict resolutions valid JSON (manaflow-ai#16071)
8473bdc fix: upload pasted images into private SSH directories (manaflow-ai#16523)
53c705c Show opt-in model, context %, and estimated cost next to agent status in the sidebar (manaflow-ai#14855)
eba3c42 remote relay: permit scoped terminal paste (manaflow-ai#14915)
e447665 fix: stop update relaunch prompts from looping (manaflow-ai#15702)
4a46320 Fix Cloud paid team limits for ID-only selected teams (manaflow-ai#16318)
c266af9 test(cloud): pin the CLI tree's link error message through the bundled CLI (manaflow-ai#16515)
0059066 Calmer focus feedback: one short pulse, no flash while typing (manaflow-ai#14894)
65930fc fix(remote): preserve tmux split metadata (manaflow-ai#16398)
512817d docs: fill missing unreleased user-facing changes (manaflow-ai#16519)
f204ade ci: nightly 120 Hz fling bench for the cmux-next agent pane (manaflow-ai#16511)
2be3b26 Remove generated custom sidebar preview art (manaflow-ai#16518)
teamleaderleo added a commit that referenced this pull request Oct 1, 2026
#14855 optional chain) (#16531)

* fix: quote SSH paste scripts with the detector's own helper

#16523 called CmuxRemoteSession's internal String.shellSingleQuoted from
the app target, which breaks main's compile. DetectedSSHSession already
has an identical private static shellSingleQuoted(_:); use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK

* fix: restore the custom upload runner's remote drop path

#16523 changed TerminalCustomUploadRunner to read
session.remotePastePolicy, but the runner has no session, so the app
target stopped compiling. Restore RemoteSessionCoordinator.remoteDropPath,
the package's compatibility entry point, which now returns the same
private-directory path shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK

* fix: drop a stray optional chain in the sidebar usage owner lookup

#14855's `.max { ... }?` chains on an already-optional element, which
the compiler rejects ("optional chain has no effect").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FrR7YbsQtGw2eFtDeyiTcK

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 2, 2026
8b8762a fix(settings): tolerate missing custom sidebar previews (manaflow-ai#16545)
30226ce fix: repair main's app compile (manaflow-ai#16523 SSH paste quoting + upload path, manaflow-ai#14855 optional chain) (manaflow-ai#16531)
1440e29 Add secure one-time CodeRouter handoff leases (manaflow-ai#10118)
77fd1d5 test(sidebar): resolve a committed conflict marker, keeping both tests (manaflow-ai#16540)
teamleaderleo added a commit that referenced this pull request Oct 2, 2026
…ed it

#16523 made every successful relay stop run a synchronous ssh (8s
timeout) under the coordinator lock to remove the session's paste
directory, even when the session never uploaded a file. That adds a
remote round trip to every remote workspace teardown and crashes
CmuxRemoteSession's intentional-cleanup tests, whose runner forbids
spawning processes. Track whether this session touched its paste
directory and only clean it up then. Drop the lifecycle test's carve-out
for the paste command, since stop no longer issues one without uploads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 2, 2026
* test: align remote paste tests with #16523's private paste directory

- Policy maintenance test: give the size-capped files distinct mtimes; with
  same-second mtimes the script's "oldest" fell back to glob order.
- emittedText fallback test: pass a cmux-paste path instead of expecting a
  string the input never contained.
- Detected SSH upload cleanup test: the first ssh call is now directory
  maintenance; assert on the last (cleanup) call and the uploaded file name,
  since cleanup addresses files through "$HOME/...".
- Cleanup lifecycle runner: treat the paste-directory teardown that follows a
  successful relay stop as neither relay cleanup nor a startup request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(remote): only clean the remote paste directory if this session used it

#16523 made every successful relay stop run a synchronous ssh (8s
timeout) under the coordinator lock to remove the session's paste
directory, even when the session never uploaded a file. That adds a
remote round trip to every remote workspace teardown and crashes
CmuxRemoteSession's intentional-cleanup tests, whose runner forbids
spawning processes. Track whether this session touched its paste
directory and only clean it up then. Drop the lifecycle test's carve-out
for the paste command, since stop no longer issues one without uploads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 2, 2026
abdf121 fix: exclude unowned same-TTY processes from memory attribution (manaflow-ai#16559)
075dbef test: fix remote paste test failures on main from manaflow-ai#16523 (manaflow-ai#16596)
7d7a9d1 Stop US key positions from hijacking shortcuts on non-US layouts (manaflow-ai#16237)
134c9d9 Let AppKit cycle windows with the System Settings shortcut on ISO keyboards (manaflow-ai#16238)
d0dd457 iOS: prevent toolbar flash when switching primary tabs (manaflow-ai#15712)
6c4b727 test(cloud): re-enable the Cloud header width tests by measuring each row (manaflow-ai#16590)
51b60f3 fix(remote): keep reconnect cleanup fixture process-free (manaflow-ai#16586)
6090053 fix(ci): reserve only queued release slots (manaflow-ai#16588)
0440a5d fix: make browser import hint cover all supported browsers (manaflow-ai#16483)
@ejc3

ejc3 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

This changed the path a terminal.uploadCommands command receives. CMUX_UPLOAD_REMOTE_PATH is now a file in ~/.cache/cmux/paste/<session>/, and that directory is created only for the built-in transport. A custom command that runs scp to the path now fails with No such file or directory, and the paste inserts nothing.

#17388 updates the docs and comments to say the command has to create the directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged-unverified A judging check was not green at merge; see the merge receipt comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants